/* Mat�as Blanco & Sergio P�rez      */
/* Los monos con monitores en Javarl */

package monkeysmon;

import java.util.logging.Level;
import java.util.logging.Logger;
import java.lang.Math;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

enum TEstadoPuente{Este, Oeste, Vacio};
enum TBrujula{Este, Oeste};



class GuyBrush{
    
    int NMonos; // Monos cruzando en un momento

    int max_seguidos = 5;
    
    int monos_este_esperando = 0;
    int monos_oeste_esperando = 0;
    
    int monos_seguidos_este = 0;
    int monos_seguidos_oeste = 0;

    public TEstadoPuente EstadoPuente;
    
    public GuyBrush(){
        this.NMonos = 0; // No cruza ninguno al principio
        EstadoPuente = TEstadoPuente.Vacio;
    }
    
    public synchronized void PasaMonoEste(){
        // Si caben m�s monos y no pasan los del oeste... (*)
        this.monos_este_esperando++;
        while (this.EstadoPuente == TEstadoPuente.Oeste || this.NMonos == 4 || (this.monos_oeste_esperando > 0 && this.monos_seguidos_este > max_seguidos ) ){
            try {
                this.wait();  // Si no, pues a esperar sentado y tranquilito ...
            } catch (InterruptedException ex) {
                Logger.getLogger(GuyBrush.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
            // (*) ... pasa
            this.monos_seguidos_este++;
            this.monos_seguidos_oeste=0;
            
            this.EstadoPuente = TEstadoPuente.Este;
            this.monos_este_esperando--;
            this.NMonos+=1; // Un mono m�s
    }

    public synchronized void PasaMonoOeste(){
        // Si caben m�s monos y no pasan los del este... (*)
        this.monos_oeste_esperando++;
        while (this.EstadoPuente == TEstadoPuente.Este || this.NMonos == 4 || (this.monos_este_esperando > 0 && this.monos_seguidos_oeste > max_seguidos )){
            try {
                this.wait(); // Si no, pues a esperar sentado y tranquilito ...
            } catch (InterruptedException ex) {
                Logger.getLogger(GuyBrush.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
            // (*) ... pasa
            this.monos_seguidos_oeste++;
            this.monos_seguidos_este=0;
            this.EstadoPuente = TEstadoPuente.Oeste;
            this.monos_oeste_esperando--;
            this.NMonos+=1; // Un mono m�s
    }
    
    public synchronized void SaleMono(int i){
        // El mono sale, hay uno menos en el puente
        this.NMonos-=1;
        // Si quedan 0, es que est� vac�o.
        if (this.NMonos==0){
            this.EstadoPuente = TEstadoPuente.Vacio;
        }
        System.out.println("Sale el mono "+i+ " (" + this.NMonos + " monos del "+ this.EstadoPuente + " en el puente ahora)");
        this.notifyAll();
        System.out.println("Oeste esperando: "+this.monos_oeste_esperando+" - Este esperando: "+this.monos_este_esperando);
    }
}


class Mono extends Thread{
    int id;
    TBrujula vieneDel;
    GuyBrush el_jefe;
    Random rnd;
    
    public Mono(int id, TBrujula vieneDel, GuyBrush el_jefe ){
        this.id = id;             // Su DNI monuno.
        this.vieneDel = vieneDel; // Ellos al menos saben de d�nde vienen.    
        this.el_jefe = el_jefe;   // El jefe, en este caso, el pirata GuyBrush bebiendo Grog.
        rnd = new Random();
    }
    
    public void run(){
        // Estas son las moner�as, o monadas... esto es, lo que hace el mono.
        System.out.println("El mono "+this.id+" LLEGA y QUIERE PASAR desde el "+this.vieneDel+".");
        // Le pide paso a GuyBrush, diciendole de donde viene ...
        // que GuyBrush siempre fue despistado para esto.
        if (this.vieneDel == TBrujula.Este)
            this.el_jefe.PasaMonoEste();
        else
            this.el_jefe.PasaMonoOeste();
        // GuyBrush le ha dejado pasar cuando llegue aqu�.
        this.cruza();
        // Y se va del puente, no sin un �Ua Uaua Ua!
        this.el_jefe.SaleMono(this.id);
    }
    
    public void cruza(){
        System.out.println("El mono "+this.id+" CRUZA desde el "+this.vieneDel+ " (" + el_jefe.NMonos + " monos del "+el_jefe.EstadoPuente+" en el puente ahora)");
        // Tarda un ratico en cruzar, aleatorio entre un segundo y seis.
        try
        {
            Mono.sleep((Math.abs(rnd.nextInt()%5000))+1000);
        }catch(InterruptedException ie) {  }
    }
}


public class Main {

    public static void main(String[] args) {
        
        System.out.println("*********************************");
        System.out.println("Monkey Island - Monitores en Java");
        System.out.println("  Mat�as Blanco & Sergio P�rez");
        System.out.println("*********************************");

        
        int i;
        int nm=50; // Numero de monos en total, se podr�a usar un argumento...
        
        Random rnd = new Random();
        
        GuyBrush Guy = new GuyBrush(); // Qu� grande este GuyBrush
        
        List lMonos = new ArrayList(); // Lista donde metemos los monos :)
        
        Mono tempMono; // Un mono temporal? Suena mal, pero nos hace falta :)
            
        for(i=1;i<=nm;i++){
                // Creamos algunos del este y otros del oeste, con un 0'5 de probabilidad
                // cada uno de ellos.
                if (Math.random() < 0.5) {
                    tempMono = new Mono(i, TBrujula.Este, Guy);
                } else {
                    tempMono = new Mono(i, TBrujula.Oeste, Guy);
                }
                tempMono.start(); // Que haga el mono pues.
                lMonos.add(tempMono); // Y a la "monera"
                
                // Esperamos un tiempo aleatorio para "lanzar" otro macaco.
                try {
                    Mono.sleep((Math.abs(rnd.nextInt() % 3000)) + 500);
                } catch (InterruptedException ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                } // ---------
                // fin del try.        
	}               
    }    
}